package calculator;

import java.util.ArrayList;
import java.util.Stack;

public class Calculator{
    //字符串转中缀表达式
    private static ArrayList<String> stringToInfixExpression(String str){
        String tempString = "";
        ArrayList<String> infixExpression = new ArrayList<>();
        for (int i = 0; i < str.length(); i++) {
            if (Character.isDigit(str.charAt(i)) || str.charAt(i) == '.') {
                tempString += str.charAt(i);
            }
            if (str.charAt(i) == '+' || str.charAt(i) == '-' || str.charAt(i) == '*' || str.charAt(i) == '/' || str.charAt(i) == '(' || str.charAt(i) == ')') {
                if (!tempString.equals(""))
                    infixExpression.add(tempString);
                tempString = "";
                infixExpression.add(str.substring(i, i + 1));
            }
        }
        infixExpression.add(new String(tempString));
        return infixExpression;
    }

    //中缀表达式转后缀表达式
    private static ArrayList<String> infixExpressionToPostfixExpression(ArrayList<String> infixExpression){
        ArrayList<String> postfixExpression = new ArrayList<>();
        Stack<String> stack = new Stack<>();
        for (String str : infixExpression) {
/*
//如果扫描到的字符是一个操作符，分三种情况：
//（1）如果堆栈是空的，直接将操作符存储到堆栈中（push it）
//（2）如果该操作符的优先级大于堆栈出口的操作符，就直接将操作符存储到堆栈中（push it）
//（3）如果该操作符的优先级低于堆栈出口的操作符，就将堆栈出口的操作符导出（pop it）,
       直到该操作符的优先级大于堆栈顶端的操作符。将扫描到的操作符导入到堆栈中（push）。
// 如果遇到的操作符是左括号"（”，就直接将该操作符输出到堆栈当中。该操作符只有在遇到右括号“)”
的时候移除。这是一个特殊符号该特殊处理。
//如果扫描到的操作符是右括号“）”，将堆栈中的操作符导出（pop）到output中输出，直到遇见左括号“（”。
将堆栈中的左括号移出堆栈（pop ）。继续扫描下一个字符
//如果输入的中缀表达式已经扫描完了，但是堆栈中仍然存在操作符的时候，我们应该讲堆栈中的操作符导出并输入到output 当中。
*/
            if (str.equals("+") || str.equals("-") || str.equals("*") || str.equals("/")) {
                if (stack.isEmpty()) {
                    stack.push(str);
                } else if (getPriority(str) > getPriority(stack.peek())) {
                    stack.push(str);
                } else {
                    while (true) {
                        if (stack.isEmpty()) {
                            stack.push(str);
                            break;
                        } else if (getPriority(str) > getPriority(stack.peek())) {
                            stack.push(str);
                            break;
                        }
                        postfixExpression.add(stack.pop());
                    }
                }
            } else if (str.equals("(")) {
                stack.push(str);
            } else if (str.equals(")")) {
                while (true) {
                    if (stack.peek().equals("(")) {
                        stack.pop();
                        break;
                    }
                    postfixExpression.add(stack.pop());
                }
            } else {
                postfixExpression.add(str);
            }
        }
        while (!stack.isEmpty()) {
            postfixExpression.add(stack.pop());
        }
        return postfixExpression;
    }

    //后缀表达式求值
    private static double evaluatePostfixExpressions(ArrayList<String> postfixExpressions){
/*
从左至右扫描表达式，遇到数字时，将数字压入堆栈，遇到运算符时，
弹出栈顶的两个数，用运算符对它们做相应的计算（次顶元素 op 栈顶元素），
并将结果入栈；重复上述过程直到表达式最右端，最后运算得出的值即为表达式的结果
*/
        Stack<Double> doubleStack = new Stack<>();
        double num1 = 0.0, num2 = 0.0, tempResult = 0.0;
        for (String string : postfixExpressions) {
            if (string.equals("+") || string.equals("-") || string.equals("*") || string.equals("/")) {
                num1 = doubleStack.pop();
                num2 = doubleStack.pop();
                switch (string) {
                    case "+":
                        tempResult = num2 + num1;
                        break;
                    case "-":
                        tempResult = num2 - num1;
                        break;
                    case "*":
                        tempResult = num2 * num1;
                        break;
                    case "/":
                        tempResult = num2 / num1;
                        break;
                    default:
                        break;
                }
                doubleStack.push(tempResult);
            } else {
                doubleStack.push(Double.parseDouble(string));
            }
        }
        return doubleStack.peek();
    }

    //获取优先级
    private static int getPriority(String op){
        int priority = 0;
        switch (op) {
            case "+":
            case "-":
                priority = 1;
                break;
            case "*":
            case "/":
                priority = 2;
                break;
            default:
                break;
        }
        return priority;
    }

    public static double calculator(String s){
        ArrayList<String> infixExpression = new ArrayList<>();
        ArrayList<String> PostfixExpression = new ArrayList<>();
        infixExpression = stringToInfixExpression(s);
        PostfixExpression = infixExpressionToPostfixExpression(infixExpression);
        //System.out.println(evaluatePostfixExpressions(PostfixExpression));
        return evaluatePostfixExpressions(PostfixExpression);
    }
}
